package net.huazai.controller;

import com.google.code.kaptcha.Producer;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import net.huazai.enums.BizCodes;
import net.huazai.enums.SendCodes;
import net.huazai.service.CaptchaService;
import net.huazai.utils.ApiResult;
import net.huazai.utils.CommonUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.concurrent.TimeUnit;

/**
 * @author 11987
 */
@Api(tags = "验证码模块")
@RestController
@RequestMapping("/api/user/captcha/v1")
@Slf4j
public class CaptchaController {

    // 图形验证码配置类
    @Autowired
    private Producer imgCaptchaProducer;

    // 定义验证码过期时间，10 分钟有效期
    private static final long CAPTCHA_EXPIRED = 1000 * 10 * 60;

    // 定义验证码 Redis key 前缀
    private static final String CAPTCHA_REDIS_KEY_PREFIX = "huazai-user:captcha:";

    // 目前验证码为 key-value 形式，直接使用 StringRedisTemplate 即可
    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 注入发送验证码类
     */
    @Autowired
    private CaptchaService captchaService;


    /**
     * 获取图形验证码
     * @param request 请求对象
     * @param response 响应对象
     */
    @ApiOperation("获取图形验证码")
    @RequestMapping("getImgCaptcha")
    public void getImgCaptcha(HttpServletRequest request, HttpServletResponse response){
        // 1、先生成图形验证码文本
        String captchaText = imgCaptchaProducer.createText();
        log.info("验证码模块-获取图形验证码文本:{}", captchaText);

        // 2、存储 Redis，配置过期时间
        redisTemplate.opsForValue().set(
                getCaptchaKey(request),  // 验证码 key
                captchaText, // 验证码内容
                CAPTCHA_EXPIRED, // 验证码过期时间
                TimeUnit.MILLISECONDS); // 时间单位毫秒

        // 3、根据验证码文本生成图形验证码图片
        BufferedImage bufferedImage = imgCaptchaProducer.createImage(captchaText);

        /*
          这里改下写法
          实现了 AutoCloseable 接⼝的类，在 try() ⾥声明该类实例的时候，在 try 结束后⾃动调⽤的 close ⽅法，这个动作会早于
          finally ⾥调⽤的⽅法，不管是否出现异常，try() ⾥的实例都会被调⽤ close ⽅法
          在 try ⾥⾯可以声明多个⾃动关闭的对象，越早声明的对象，会越晚被 close 掉
         */
        try (ServletOutputStream outputStream = response.getOutputStream()){
            ImageIO.write(bufferedImage, "jpg", outputStream);
            outputStream.flush();
        } catch (IOException e) {
            log.error("验证码模块-获取图形验证码异常:{}", e.getMessage());
        }
    }

    /**
     * 这里需要对用户进行唯一识别，方式就是获取用户的 IP + 浏览器的 User-Agent 属性
     * @param request 请求对象
     * @return 验证码 key
     */
    private String getCaptchaKey(HttpServletRequest request){
        // 获取客户端用户的 IP 地址
        String ip = CommonUtil.getRemoteIpAddr(request);
        // 获取请求头中的 User-Agent 属性值
        String userAgent = request.getHeader("User-Agent");

        // 根据 ip + userAgent 生成对应的 key
        String keyGen = CAPTCHA_REDIS_KEY_PREFIX + CommonUtil.MD5(ip + userAgent);

        log.info("验证码模块-验证码 key:{}", keyGen);
        return keyGen;
    }

    /**
     * 发送注册验证码
     * 1、先从缓存中获取用户的验证码
     * 2、匹配提交过来的图形验证码是否正确
     * 3、发送验证码
     * @param to 接收方
     * @param captcha 图形验证码
     * @return ApiResult
     */
    @ApiOperation("发送注册验证码")
    @PostMapping("sendCode")
    public ApiResult sendRegisterCode(
            @RequestParam(value = "to", required = true) String to
            , @RequestParam(value = "captcha", required = true) String captcha
            , HttpServletRequest request) {
        // 1、先获取用户唯一标识
        String keyGen = getCaptchaKey(request);
        // 从 Redis 中获取对应的验证码
        String cacheCaptcha = redisTemplate.opsForValue().get(keyGen);

        // 2、匹配提交过来的图形验证码是否正确
        if (captcha != null && captcha.equalsIgnoreCase(cacheCaptcha)) {
            // 匹配成功
            // 用户注册类型下的发送验证码
            return captchaService.sendCode(SendCodes.USER_REGISTER, to);
        } else {
            // 匹配失败
            return ApiResult.doResult(BizCodes.USER_CODE_CAPTCHA_ERROR);
        }
    }
}
